package com.echo.im.engin.pipeline;

import com.echo.im.client.IMClient;
import com.echo.im.common.contant.Command;
import com.echo.im.common.dto.MessageDTO;
import com.echo.im.client.model.IMUserInfo;
import com.echo.im.client.model.IMMessage;
import com.echo.im.common.vo.GroupMessageVO;
import com.echo.im.common.vo.PrivateMessageVO;
import com.echo.im.engin.packet.MessagePacket;
import com.echo.im.common.vo.MessageVO;
import com.echo.im.infrastructure.generator.IdGenerator;
import com.echo.im.infrastructure.mq.event.producer.EventPublisher;
import com.echo.im.infrastructure.session.SessionContext;
import com.echo.im.infrastructure.session.UserSession;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 消息处理流水线抽象类
 * 定义消息处理的模板方法和各个步骤的抽象接口，子类需要实现这些接口来定义具体的处理逻辑。
 *
 * @param <P> 消息包类型，继承自 MessagePacket
 * @param <T> 消息视图对象类型，继承自 MessageVO
 *
 * @author echo
 * @time 2024-08-23
 */
@Component
@Slf4j
public abstract class AbstractMessageProcessPipeline<P extends MessagePacket, T extends MessageVO> implements MessageProcessPipeline<P> {


    /**
     * 消息ID生成器，用于生成唯一的消息ID
     */
    @Autowired
    protected IdGenerator idGenerator;

    /**
     * IM客户端，用于发送消息
     */
    @Autowired
    private IMClient imClient;

    /**
     * 事件发布器，用于发布消息处理后的事件
     */
    @Autowired
    protected EventPublisher eventPublisher;

    /**
     * 消息处理的模板方法
     * 定义了消息处理的完整流程，包括用户校验、消息校验、预处理、消息创建、持久化、消息发送和后处理等步骤。
     *
     * @param dto 消息数据传输对象，包含消息包和处理需要的各种标志位
     */
    public final void processMessage(MessageDTO<P> dto) {
        // 获取当前用户的会话信息
        UserSession session = SessionContext.getSession();

        // 校验用户权限和合法性
        validateUser(session, dto);

        // 校验消息的合法性
        validateMessage(dto.getPacket());

        // 预处理，子类可以覆盖默认实现
        preprocess(dto);

        // 创建消息视图对象 //创建视图之后Packet就没用了
        T messageVO = createMessageVO(session, dto);

        // 判断是否需要生成ID
        if (dto.getIsNeedId()) {
            messageVO.setId(idGenerator.getMsgId());
        }

        // 判断是否需要持久化存储
        if (dto.getIsNeedStore()) {
            persistMessage(messageVO);
        }

        // 获取接收者ID列表
        List<Long> recvIds = getRecvIds(messageVO);

        // 处理消息发送
        processMessage(session, messageVO, dto, recvIds);

        // 后处理，子类可以覆盖默认实现
        postprocess(dto);
    }


    /**
     * 校验用户权限和合法性
     *
     * @param session 当前用户的会话信息
     * @param dto     消息数据传输对象
     */
    protected abstract void validateUser(UserSession session, MessageDTO<P> dto);

    /**
     * 校验消息的合法性
     * 此方法在模板方法中调用，可在此方法中实现基本的消息校验逻辑。
     *
     * @param packet 消息包
     */
    private void validateMessage(P packet) {
        // 默认实现：可以添加基础的消息校验逻辑
    }

    /**
     * 消息预处理
     * 子类可以覆盖此方法以添加自定义的预处理逻辑。
     *
     * @param dto 消息数据传输对象
     */
    protected void preprocess(MessageDTO<P> dto) {
        // 默认实现：无需预处理则保持空实现
    }

    /**
     * 创建消息视图对象
     * 需要子类实现，用于将消息包转化为前端可用的消息视图对象。
     *
     * @param session 当前用户的会话信息
     * @param packet     消息数据传输对象
     * @return 消息视图对象
     */
    protected abstract T createMessageVO(UserSession session, MessageDTO<P> packet);

    /**
     * 获取接收者ID列表
     * 需要子类实现，用于指定消息的接收者。
     *
     * @param messageVO 消息视图对象
     * @return 接收者ID列表
     */
    protected abstract List<Long> getRecvIds(T messageVO);

    /**
     * 处理消息的发送
     * 将消息交给IM客户端进行发送，并附带必要的元信息。
     *
     * @param session    当前用户的会话信息
     * @param messageVO  消息视图对象
     * @param dto        消息数据传输对象
     * @param recvIds    接收者ID列表
     */
    private void processMessage(UserSession session, T messageVO, MessageDTO<P> dto, List<Long> recvIds) {
        Byte cmd=null;
        if(messageVO instanceof PrivateMessageVO){
            cmd=Command.PRIVATE_OUT_MESSAGE;
        } else if (messageVO instanceof GroupMessageVO) {
            cmd=Command.GROUP_OUT_MESSAGE;
        }else {
            throw new RuntimeException("不支持的消息类型");
        }
        IMMessage<Object> imMessage = new IMMessage<>(messageVO.getId(), messageVO.getSeqId(), cmd, dto.getIsNeedOfflineStore());
        imMessage.setSender(new IMUserInfo(session.getUserId(), session.getTerminal()));
        imMessage.setRecvIds(recvIds);
        imMessage.setSendToSelf(dto.getIsNeedSendToSelf());
        imMessage.setData(messageVO);
        imMessage.setIsNeedOfflineStore(dto.getIsNeedOfflineStore());
        imClient.sendMessage(imMessage);
    }

    /**
     * 持久化消息
     * 需要子类实现，用于将消息持久化到存储系统中。
     *
     * @param messageVO 消息视图对象
     */
    protected abstract void persistMessage(T messageVO);

    /**
     * 消息处理的后续操作
     * 子类可以覆盖此方法以添加自定义的后处理逻辑。
     *
     * @param dto 消息数据传输对象
     */
    protected void postprocess(MessageDTO<P> dto) {
        // 默认实现：无需后处理则保持空实现
    }

}
